#include "nnsliteblist.h"
#include "nnslitememory.h"

static nnsliteBlistNode* CreateNode(nnsliteBlist* blist)
{
	nnsliteBlistNode* newnode;
	nnsliteMemoryContext* mc = blist->mc;
	assert(mc != NULL);
	newnode = (nnsliteBlistNode*)mc->allocMem(sizeof(nnsliteBlistNode)+blist->node_cap*blist->elemsize-1);
	if (newnode == NULL)
	{
		assert(false);
		return NULL;
	}
	newnode->blist = blist;
	newnode->next = NULL;
	newnode->usedcount = 0;
	newnode->capacity = blist->node_cap;
	return newnode;
}

static nnsliteBlistNode* GetPredecessor(nnsliteBlist* blist, nnsliteBlistNode* node)
{
	nnsliteBlistNode* prenode;
	
	assert(node != NULL);
	
	/* If this node is the first node, so it has no predecessor */
	if (node == blist->firstNode)
		return NULL;

	prenode = blist->firstNode;
	while(prenode != NULL)
	{
		if (prenode->next == node)
			return prenode;
		prenode = prenode->next;
	}
	/* Not found */
	return NULL; 
}

static NNSLITE_RET RemoveNode(nnsliteBlist* blist, nnsliteBlistNode* node)
{
	nnsliteMemoryContext* mc = blist->mc;
	assert(node != NULL && mc != NULL);
	if (node == blist->firstNode)
	{
		/* The blist must have at least 1 node */
		if (blist->lastNode == blist->firstNode)
		{
			blist->totalcount -= node->usedcount;
			node->usedcount -= node->usedcount;
		}
		else
		{
			blist->firstNode = node->next;
			blist->totalcount -= node->usedcount;
			mc->freeMem(node);
		}
	}
	else
	{
		nnsliteBlistNode* prenode = GetPredecessor(blist, node);
		assert(prenode != NULL);
		prenode->next = node->next;
		blist->totalcount -= node->usedcount;
		if (node == blist->lastNode)
			blist->lastNode = prenode;
		mc->freeMem(node);
	}
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteBlistCreate(nnsliteBlist* blist, u32 elemsize, u32 node_cap, struct nnsliteMemoryContext* mc)
{
	blist->mc = mc;
	blist->elemsize = elemsize;
	blist->totalcount = 0;
	blist->node_cap = node_cap;
	if (blist->mc == NULL)
		blist->mc = nnsliteMemoryGetDefaultContext();
	/* Create the first node */
	blist->firstNode = CreateNode(blist); 
	blist->lastNode = blist->firstNode;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteBlistFree(nnsliteBlist* blist)
{
	nnsliteMemoryContext* mc = blist->mc;
	nnsliteBlistNode* node = blist->firstNode;
	nnsliteBlistNode* nextnode;
	while(node != NULL)
	{
		nextnode = node->next;
		mc->freeMem(node);
		node = nextnode;
	}
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteBlistPushback(nnsliteBlist* blist, const void* elem)
{
	nnsliteBlistNode* lastNode = blist->lastNode;
	u32 curcount = lastNode->usedcount;
	memcpy(&lastNode->buf[curcount*blist->elemsize], elem, blist->elemsize);
	/* Increase the counters */
	lastNode->usedcount++;
	blist->totalcount++;
	/* If the last node is full, create another node */
	if (lastNode->usedcount == lastNode->capacity)
	{
		nnsliteBlistNode* newnode = CreateNode(blist);
		lastNode->next= newnode;
	}
	return NNSLITE_RET_OK;
}
	

NNSLITE_RET nnsliteBlistInsert(nnsliteBlist* blist, const void* elem)
{
	u32 curcount;
	nnsliteBlistNode* node = blist->firstNode;
	nnsliteBlistNode* lastNode = blist->lastNode;
	/* Find a unfull node */
	while(node->usedcount == node->capacity)
	{
		assert(node != NULL);
		node = node->next;
	}
	curcount = node->usedcount;
	memcpy(&node->buf[curcount*blist->elemsize], elem, blist->elemsize);
	node->usedcount++;
	blist->totalcount++;
	/* If the last node is full, create another node */
	if (node == lastNode && lastNode->usedcount == lastNode->capacity)
	{
		nnsliteBlistNode* newnode = CreateNode(blist);
		lastNode->next= newnode;
		blist->lastNode = newnode;
	}
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteBlistBegin(nnsliteBlist* blist, nnsliteBlistIterator* iter)
{
	iter->blist = blist;
	iter->curnode = blist->firstNode;
	iter->curindexInNode = U32_NULL; /* U32_NULL indicates '-1' */
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteBlistNext(nnsliteBlistIterator* iter, void* elem)
{
	u32 elemsize = iter->blist->elemsize;
	if (iter->curindexInNode == U32_NULL)
		iter->curindexInNode = 0;
	else
		iter->curindexInNode++;
	while (iter->curindexInNode == iter->curnode->usedcount)
	{
		if (iter->curnode->next == NULL)
		{
			/* No more node to iterate*/
			return NNSLITE_RET_FALSE;
		}
		else
		{
			/* Jump to the next node */
			iter->curnode = iter->curnode->next;
			iter->curindexInNode = 0;
		}
	}
	/* The user may not want to get the element' data when the 'elem' is NULL */
	if (elem != NULL)
	{
		u32 idx = iter->curindexInNode;
		nnsliteBlistNode* curnode = iter->curnode;
		memcpy(elem, &(curnode->buf[idx*elemsize]), elemsize);
	}
	return NNSLITE_RET_TRUE;
}

NNSLITE_RET nnsliteBlistRemove(nnsliteBlistIterator* iter)
{
	NNSLITE_RET ret;
	nnsliteBlistNode* curnode = iter->curnode;
	u32 idx = iter->curindexInNode;
	u32 movcount = curnode->usedcount - idx - 1;
	if (movcount > 0)
	{
		u32 elemsize = iter->blist->elemsize;
		memmove(&curnode->buf[idx*elemsize], &curnode->buf[(idx+1)*elemsize], movcount*elemsize);
	}
	curnode->usedcount--;
	iter->blist->totalcount--;
	if (curnode->usedcount == 0)
	{
		/* Remove the whole node */
		ret = RemoveNode(iter->blist, curnode);
		NNSLITE_ON_FAILED_RETURN(ret);
	}
	return NNSLITE_RET_OK;
}



